package exredis

import "github.com/gomodule/redigo/redis"

// APPEND key value
// 如果 key 已经存在并且是一个字符串， APPEND 命令将 value 追加到 key 原来的值的末尾。
// 如果 key 不存在， APPEND 就简单地将给定 key 设为 value ，就像执行 SET key value 一样。
func (r *Redis) APPEND(key, value string) (int64, error) {
	return redis.Int64(r.PreDo("APPEND", key, value))
}

// BITCOUNT key [start] [end]
// 计算给定字符串中，被设置为 1 的比特位的数量。
// 一般情况下，给定的整个字符串都会被进行计数，通过指定额外的 start 或 end 参数，可以让计数只在特定的位上进行。
// start 和 end 参数的设置和 GETRANGE 命令类似，都可以使用负数值： 比如 -1 表示最后一个字节， -2 表示倒数第二个字节，以此类推。
// 不存在的 key 被当成是空字符串来处理，因此对一个不存在的 key 进行 BITCOUNT 操作，结果为 0 。
func (r *Redis) BITCOUNT(key string) (int64, error) {
	return redis.Int64(r.PreDo("BITCOUNT", key))
}
func (r *Redis) BITCOUNTWithStartEnd(key string, start, end int64) (int64, error) {
	return redis.Int64(r.PreDo("BITCOUNT", key, start, end))
}

// BITOP operation destkey key [key ...]
// 对一个或多个保存二进制位的字符串 key 进行位元操作，并将结果保存到 destkey 上。
// operation 可以是 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种：
// BITOP AND destkey key [key ...] ，对一个或多个 key 求逻辑并，并将结果保存到 destkey 。
// BITOP OR destkey key [key ...] ，对一个或多个 key 求逻辑或，并将结果保存到 destkey 。
// BITOP XOR destkey key [key ...] ，对一个或多个 key 求逻辑异或，并将结果保存到 destkey 。
// BITOP NOT destkey key ，对给定 key 求逻辑非，并将结果保存到 destkey 。
// 除了 NOT 操作之外，其他操作都可以接受一个或多个 key 作为输入。
func (r *Redis) BITOP(args ...interface{}) (int64, error) {
	return redis.Int64(r.PreDo("BITOP", args...))
}

//TODO:BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment] [OVERFLOW WRAP|SAT|FAIL]
func (r *Redis) BITFIELD() {
	//TODO:
}

// DECR key
// 将 key 中储存的数字值减一。
// 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 DECR 操作。
// 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。
// 本操作的值限制在 64 位(bit)有符号数字表示之内。
func (r *Redis) DECR(key string) (int64, error) {
	return redis.Int64(r.PreDo("DECR", key))
}

// DECRBY key decrement
// 将 key 所储存的值减去减量 decrement 。
// 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 DECRBY 操作。
// 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。
// 本操作的值限制在 64 位(bit)有符号数字表示之内。
func (r *Redis) DECRBY(key string, decrement int64) (int64, error) {
	return redis.Int64(r.PreDo("DECRBY", key, decrement))
}

// GET key
// 返回 key 所关联的字符串值。
// 如果 key 不存在那么返回特殊值 nil 。
// 假如 key 储存的值不是字符串类型，返回一个错误，因为 GET 只能用于处理字符串值。
func (r *Redis) GET(key string) (string, error) {
	return redis.String(r.PreDo("GET", key))
}

// GETBIT key offset
// 对 key 所储存的字符串值，获取指定偏移量上的位(bit)。
// 当 offset 比字符串值的长度大，或者 key 不存在时，返回 0 。
func (r *Redis) GETBIT(key string, offset int64) (int, error) {
	return redis.Int(r.PreDo("GETBIT", key, offset))
}

// GETRANGE key start end
// 返回 key 中字符串值的子字符串，字符串的截取范围由 start 和 end 两个偏移量决定(包括 start 和 end 在内)。
// 负数偏移量表示从字符串最后开始计数， -1 表示最后一个字符， -2 表示倒数第二个，以此类推。
// GETRANGE 通过保证子字符串的值域(range)不超过实际字符串的值域来处理超出范围的值域请求。
func (r *Redis) GETRANGE(key string, start, end int64) (string, error) {
	return redis.String(r.PreDo("GETRANGE", key, start, end))
}

// GETSET key value
// 将给定 key 的值设为 value ，并返回 key 的旧值(old value)。
// 当 key 存在但不是字符串类型时，返回一个错误。
func (r *Redis) GETSET(key, value string) (string, error) {
	return redis.String(r.PreDo("GETSET", key, value))
}

// INCR key
// 将 key 中储存的数字值增一。
// 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 INCR 操作。
// 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。
// 本操作的值限制在 64 位(bit)有符号数字表示之内。
func (r *Redis) INCR(key string) (int64, error) {
	return redis.Int64(r.PreDo("INCR", key))
}

// INCRBY key increment
// 将 key 所储存的值加上增量 increment 。
// 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 INCRBY 命令。
// 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。
// 本操作的值限制在 64 位(bit)有符号数字表示之内。
func (r *Redis) INCRBY(key string, decrement int64) (int64, error) {
	return redis.Int64(r.PreDo("INCRBY", key, decrement))
}

// INCRBYFLOAT key increment
// 为 key 中所储存的值加上浮点数增量 increment 。
// 如果 key 不存在，那么 INCRBYFLOAT 会先将 key 的值设为 0 ，再执行加法操作。
// 如果命令执行成功，那么 key 的值会被更新为（执行加法之后的）新值，并且新值会以字符串的形式返回给调用者。
// 无论是 key 的值，还是增量 increment ，都可以使用像 2.0e7 、 3e5 、 90e-2 那样的指数符号(exponential notation)来表示，但是，执行 INCRBYFLOAT 命令之后的值总是以同样的形式储存，也即是，它们总是由一个数字，一个（可选的）小数点和一个任意位的小数部分组成（比如 3.14 、 69.768 ，诸如此类)，小数部分尾随的 0 会被移除，如果有需要的话，还会将浮点数改为整数（比如 3.0 会被保存成 3 ）。
// 除此之外，无论加法计算所得的浮点数的实际精度有多长， INCRBYFLOAT 的计算结果也最多只能表示小数点的后十七位。
// 当以下任意一个条件发生时，返回一个错误：
// key 的值不是字符串类型(因为 Redis 中的数字和浮点数都以字符串的形式保存，所以它们都属于字符串类型）
// key 当前的值或者给定的增量 increment 不能解释(parse)为双精度浮点数(double precision floating point number）
func (r *Redis) INCRBYFLOAT(key string, decrement float32) (string, error) {
	return redis.String(r.PreDo("INCRBYFLOAT", key, decrement))
}

// MGET key [key ...]
// 返回所有(一个或多个)给定 key 的值。
// 如果给定的 key 里面，有某个 key 不存在，那么这个 key 返回特殊值 nil 。因此，该命令永不失败。
func (r *Redis) MGET(args ...interface{}) ([]string, error) {
	return redis.Strings(r.PreDo("MGET", args...))
}

// MSET key value [key value ...]
// 同时设置一个或多个 key-value 对。
// 如果某个给定 key 已经存在，那么 MSET 会用新值覆盖原来的旧值，如果这不是你所希望的效果，请考虑使用 MSETNX 命令：它只会在所有给定 key 都不存在的情况下进行设置操作。
// MSET 是一个原子性(atomic)操作，所有给定 key 都会在同一时间内被设置，某些给定 key 被更新而另一些给定 key 没有改变的情况，不可能发生。
func (r *Redis) MSET(args ...interface{}) {
	r.PreDo("MSET", args...)
}

// MSETNX key value [key value ...]
// 同时设置一个或多个 key-value 对，当且仅当所有给定 key 都不存在。
// 即使只有一个给定 key 已存在， MSETNX 也会拒绝执行所有给定 key 的设置操作。
// MSETNX 是原子性的，因此它可以用作设置多个不同 key 表示不同字段(field)的唯一性逻辑对象(unique logic object)，所有字段要么全被设置，要么全不被设置。
func (r *Redis) MSETNX(args ...interface{}) (int, error) {
	return redis.Int(r.PreDo("MSETNX", args...))
}

// PSETEX key milliseconds value
// 这个命令和 SETEX 命令相似，但它以毫秒为单位设置 key 的生存时间，而不是像 SETEX 命令那样，以秒为单位。
func (r *Redis) PSETEX(key string, milliseconds int64, value string) (bool, error) {
	return IsOK(redis.String(r.PreDo("PSETEX", key, milliseconds, value)))
}

// SET key value [EX seconds] [PX milliseconds] [NX|XX]
// 将字符串值 value 关联到 key 。
// 如果 key 已经持有其他值， SET 就覆写旧值，无视类型。
// 对于某个原本带有生存时间（TTL）的键来说， 当 SET 命令成功在这个键上执行时， 这个键原有的 TTL 将被清除。
func (r *Redis) SET(args ...interface{}) (int, error) {
	return redis.Int(r.PreDo("SET", args...))
}

// SETBIT key offset value
// 对 key 所储存的字符串值，设置或清除指定偏移量上的位(bit)。
// 位的设置或清除取决于 value 参数，可以是 0 也可以是 1 。
// 当 key 不存在时，自动生成一个新的字符串值。
// 字符串会进行伸展(grown)以确保它可以将 value 保存在指定的偏移量上。当字符串值进行伸展时，空白位置以 0 填充。
// offset 参数必须大于或等于 0 ，小于 2^32 (bit 映射被限制在 512 MB 之内)。
func (r *Redis) SETBIT(key string, offset int64, value int) (int, error) {
	return redis.Int(r.PreDo("SETBIT", key, offset, value))
}

// SETEX key seconds value
// 将值 value 关联到 key ，并将 key 的生存时间设为 seconds (以秒为单位)。
// 如果 key 已经存在， SETEX 命令将覆写旧值。
// 这个命令类似于以下两个命令：
// SET key value
// EXPIRE key seconds  # 设置生存时间
// 不同之处是， SETEX 是一个原子性(atomic)操作，关联值和设置生存时间两个动作会在同一时间内完成，该命令在 Redis 用作缓存时，
func (r *Redis) SETEX(key string, seconds int64, value string) (bool, error) {
	return IsOK(redis.String(r.PreDo("SETEX", key, seconds, value)))
}

// SETNX key value
// 将 key 的值设为 value ，当且仅当 key 不存在。
// 若给定的 key 已经存在，则 SETNX 不做任何动作。
// SETNX 是『SET if Not eXists』(如果不存在，则 SET)的简写。
func (r *Redis) SETNX(key string, value string) (bool, error) {
	return redis.Bool(r.PreDo("SETNX", key, value))
}

// SETRANGE key offset value
// 用 value 参数覆写(overwrite)给定 key 所储存的字符串值，从偏移量 offset 开始。
// 不存在的 key 当作空白字符串处理。
// SETRANGE 命令会确保字符串足够长以便将 value 设置在指定的偏移量上，如果给定 key 原来储存的字符串长度比偏移量小(比如字符串只有 5 个字符长，但你设置的 offset 是 10 )，那么原字符和偏移量之间的空白将用零字节(zerobytes, "\x00" )来填充。
// 注意你能使用的最大偏移量是 2^29-1(536870911) ，因为 Redis 字符串的大小被限制在 512 兆(megabytes)以内。如果你需要使用比这更大的空间，你可以使用多个 key 。
func (r *Redis) SETRANGE(key string, offset int64, value string) (int, error) {
	return redis.Int(r.PreDo("SETRANGE", key, offset, value))
}

// STRLEN key
// 返回 key 所储存的字符串值的长度。
// 当 key 储存的不是字符串值时，返回一个错误。
func (r *Redis) STRLEN(key string) (int, error) {
	return redis.Int(r.PreDo("STRLEN", key))
}
